﻿using System.Buffers;
using System.IO.Compression;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;

namespace System.NETool;

public static class ReadOnlySpanEx
{
    /// <summary>
    /// 获取数组十六进制字符串值
    /// </summary>
    /// <param name="this">数组</param>
    /// <param name="span">分隔符</param>
    /// <returns>字符串</returns>
    public static string ToHexString(this ReadOnlySpan<byte> @this, string span = "")
    {
        using var sb = new ValueStringBuilder(1024);
        foreach (var bt in @this)
        {
            sb.Append(bt.ToString("X2"));
            if (!string.IsNullOrEmpty(span)) sb.Append(span);
        }

        return sb.ToString();
    }

    /// <summary>
    /// 数组转结构对象
    /// </summary>
    /// <typeparam name="T">结构类型</typeparam>
    /// <param name="this">数组</param>
    /// <returns>转结构对象</returns>
    /// <exception cref="ArgumentException">当数组长度小于结构体大小时抛出</exception>
    public static T ToStruct<T>(this ReadOnlySpan<byte> @this) where T : struct
    {
        int size = Marshal.SizeOf<T>();
        if (size > @this.Length)
        {
            throw new ArgumentException("Span is too small.");
        }

        // 分配结构体大小的内存空间
        IntPtr structPtr = Marshal.AllocHGlobal(size);
        byte[] bytes = ArrayPool<byte>.Shared.Rent(@this.Length);
        @this.CopyTo(bytes.AsSpan());
        // 将byte数组拷到分配好的内存空间
        Marshal.Copy(bytes, 0, structPtr, size);
        // 将内存空间转换为目标结构体
        var obj = Marshal.PtrToStructure<T>(structPtr);
        // 释放内存空间
        Marshal.FreeHGlobal(structPtr);
        ArrayPool<byte>.Shared.Return(bytes);
        // 返回结构体
        return obj;
    }

    /// <summary>
    /// 将结构体数组转换为字节数组
    /// </summary>
    /// <typeparam name="T">结构体类型</typeparam>
    /// <param name="this">结构体数组</param>
    /// <returns>字节数组</returns>
    public static ReadOnlySpan<byte> AsBytes<T>(this ReadOnlySpan<T> @this) where T : struct
    {
        return MemoryMarshal.AsBytes(@this);
    }

    /// <summary>
    /// 将一种结构体数组转换为另一种结构体数组
    /// </summary>
    /// <typeparam name="TFrom">源结构体类型</typeparam>
    /// <typeparam name="TTo">目标结构体类型</typeparam>
    /// <param name="this">源结构体数组</param>
    /// <returns>目标结构体数组</returns>
    public static ReadOnlySpan<TTo> Cast<TFrom, TTo>(this ReadOnlySpan<TFrom> @this) where TFrom : struct where TTo : struct
    {
        return MemoryMarshal.Cast<TFrom, TTo>(@this);
    }

    /// <summary>
    /// 获取数组的十六进制打印字符串
    /// </summary>
    /// <param name="this">数组</param>
    /// <param name="title">标题</param>
    /// <returns>十六进制打印字符串</returns>
    public static string HexPrintString(this in ReadOnlySpan<byte> @this, string title)
    {
        using var sb = new ValueStringBuilder(1024);
        sb.AppendLine(($"-- {title} ").PadRight(60, '-'));
        int idx = 0;
        while (idx < @this.Length)
        {
            string str = @this.Slice(idx, Math.Min(@this.Length - idx, 16)).ToHexString(" ");
            if (str.Length >= 24) str = str.Left(24) + " " + str.Middle(24, str.Length);
            sb.AppendLine(BigEndian(idx).ToHexString() + " | " + str);
            idx += 16;
        }

        sb.Append("-".Repeat(60));
        return sb.ToString();
    }

    /// <summary>
    /// 打印数组的十六进制表示
    /// </summary>
    /// <param name="this">数组</param>
    /// <param name="title">标题</param>
    /// <param name="color">控制台颜色</param>
    public static void HexPrint(this in ReadOnlySpan<byte> @this, string title, ConsoleColor color = ConsoleColor.Blue)
    {
        ConsoleEx.WriteLine(HexPrintString(@this, title), color);
    }

    /// <summary>
    /// 将整数转换为大端字节数组
    /// </summary>
    /// <param name="value">整数值</param>
    /// <returns>大端字节数组</returns>
    internal static byte[] BigEndian(int value)
    {
        var buffer = new byte[4];
        buffer[3] = (byte)value;
        buffer[2] = (byte)(value >> 8);
        buffer[1] = (byte)(value >> 16);
        buffer[0] = (byte)(value >> 24);
        return buffer;
    }

    /// <summary>
    /// ASCII编码的数组转换为英文字符串
    /// </summary>
    /// <param name="this">数组</param>
    /// <returns>结果</returns>
    public static string ToEnString(this ReadOnlySpan<byte> @this) => Encoding.ASCII.GetString(@this).Trim('\0').Trim();

    /// <summary>
    /// 计算字节数组的MD5哈希值并返回十六进制字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <returns>MD5哈希值的十六进制字符串</returns>
    public static string MD5(this ReadOnlySpan<byte> @this) => Security.Cryptography.MD5.HashData(@this).ToHexString();

    /// <summary>
    /// 计算字节数组的SHA1哈希值并返回十六进制字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <returns>SHA1哈希值的十六进制字符串</returns>
    public static string SHA1(this ReadOnlySpan<byte> @this) => Security.Cryptography.SHA1.HashData(@this).ToHexString();

    /// <summary>
    /// 计算字节数组的SHA256哈希值并返回十六进制字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <returns>SHA256哈希值的十六进制字符串</returns>
    public static string SHA256(this ReadOnlySpan<byte> @this) => Security.Cryptography.SHA256.HashData(@this).ToHexString();

    /// <summary>
    /// 计算字节数组的SHA384哈希值并返回十六进制字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <returns>SHA384哈希值的十六进制字符串</returns>
    public static string SHA384(this ReadOnlySpan<byte> @this) => Security.Cryptography.SHA384.HashData(@this).ToHexString();

    /// <summary>
    /// 计算字节数组的SHA512哈希值并返回十六进制字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <returns>SHA512哈希值的十六进制字符串</returns>
    public static string SHA512(this ReadOnlySpan<byte> @this) => Security.Cryptography.SHA512.HashData(@this).ToHexString();

    /// <summary>
    /// 计算字节数组的SHA3_256哈希值并返回十六进制字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <returns>SHA3_256哈希值的十六进制字符串</returns>
    public static string SHA3_256(this ReadOnlySpan<byte> @this) => Security.Cryptography.SHA3_256.HashData(@this).ToHexString();

    /// <summary>
    /// 计算字节数组的SHA3_384哈希值并返回十六进制字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <returns>SHA3_384哈希值的十六进制字符串</returns>
    public static string SHA3_384(this ReadOnlySpan<byte> @this) => Security.Cryptography.SHA3_384.HashData(@this).ToHexString();

    /// <summary>
    /// 计算字节数组的SHA3_512哈希值并返回十六进制字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <returns>SHA3_512哈希值的十六进制字符串</returns>
    public static string SHA3_512(this ReadOnlySpan<byte> @this) => Security.Cryptography.SHA3_512.HashData(@this).ToHexString();

    /// <summary>
    /// 生成PBKDF2哈希字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <param name="iterationCount">迭代次数</param>
    /// <param name="derivedKeyLength">派生密钥长度</param>
    /// <returns>PBKDF2哈希字符串</returns>
    public static string PBKDF2(this ReadOnlySpan<byte> @this, int iterationCount = 1000, int derivedKeyLength = 32) =>
        @this.ToHexString().PBKDF2(iterationCount, derivedKeyLength);

    /// <summary>
    /// 验证PBKDF2哈希字符串
    /// </summary>
    /// <param name="this">字节数组</param>
    /// <param name="hash">PBKDF2哈希字符串</param>
    /// <param name="iterationCount">迭代次数</param>
    /// <returns>验证结果</returns>
    public static bool PBKDF2Verify(this ReadOnlySpan<byte> @this, string hash, int iterationCount = 1000) =>
        @this.ToHexString().PBKDF2Verify(hash, iterationCount);

    /// <summary>
    /// 使用AES算法加密字节数组
    /// </summary>
    /// <param name="this">要加密的明文字节数组</param>
    /// <param name="key">加密密钥</param>
    /// <param name="iv">初始化向量</param>
    /// <returns>加密后的字节数组</returns>
    public static byte[] AesEncrypt(this ReadOnlySpan<byte> @this, string key = "", string iv = "")
    {
        if (@this.Length <= 0)
            throw new ArgumentNullException(nameof(@this));

        // 创建一个Aes对象
        using var aes = Aes.Create();
        var pair = aes.GetKeyIv(key, iv);

        // 创建一个加密器来执行流转换
        var encryptor = aes.CreateEncryptor(pair.key, pair.iv);

        // 创建用于加密的流
        using var msEncrypt = new MemoryStream();
        using var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
        csEncrypt.Write(@this);
        csEncrypt.FlushFinalBlock();
        return msEncrypt.ToArray();
    }

    /// <summary>
    /// 使用AES算法解密字节数组
    /// </summary>
    /// <param name="this">要解密的字节数组</param>
    /// <param name="key">解密密钥</param>
    /// <param name="iv">初始化向量</param>
    /// <returns>解密后的字节数组</returns>
    public static byte[] AesDecrypt(this ReadOnlySpan<byte> @this, string key = "", string iv = "")
    {
        // 检查参数
        if (@this.Length <= 0)
            throw new ArgumentNullException(nameof(@this));

        // 创建一个Aes对象
        using var aes = Aes.Create();
        var pair = aes.GetKeyIv(key, iv);

        // 创建一个解密器来执行流转换
        var decryptor = aes.CreateDecryptor(pair.key, pair.iv);

        // 创建用于解密的流
        using var msDecrypt = new MemoryStream(@this.ToArray());
        using var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);

        // 从解密流中读取解密后的字节并将其放入字节数组中
        using var originalStream = new MemoryStream();
        var buffer = new byte[256];
        int readBytes;
        while ((readBytes = csDecrypt.Read(buffer, 0, buffer.Length)) > 0)
        {
            originalStream.Write(buffer, 0, readBytes);
        }

        originalStream.Flush();
        return originalStream.ToArray();
    }

    /// <summary>
    /// 使用DES算法加密字节数组
    /// </summary>
    /// <param name="this">要加密的明文字节数组</param>
    /// <param name="key">加密密钥</param>
    /// <param name="iv">初始化向量</param>
    /// <returns>加密后的字节数组</returns>
    public static byte[] DesEncrypt(this ReadOnlySpan<byte> @this, string key = "", string iv = "")
    {
        if (@this.Length <= 0)
            throw new ArgumentNullException(nameof(@this));

        // 创建一个Des对象
        using var des = DES.Create();
        var pair = des.GetKeyIv(key, iv);

        // 创建一个加密器来执行流转换
        var encryptor = des.CreateEncryptor(pair.key, pair.iv);

        // 创建用于加密的流
        using var msEncrypt = new MemoryStream();
        using var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write);
        csEncrypt.Write(@this);
        csEncrypt.FlushFinalBlock();
        return msEncrypt.ToArray();
    }

    /// <summary>
    /// 使用DES算法解密字节数组
    /// </summary>
    /// <param name="this">要解密的字节数组</param>
    /// <param name="key">解密密钥</param>
    /// <param name="iv">初始化向量</param>
    /// <returns>解密后的字节数组</returns>
    public static byte[] DesDecrypt(this ReadOnlySpan<byte> @this, string key = "", string iv = "")
    {
        // 检查参数
        if (@this.Length <= 0)
            throw new ArgumentNullException(nameof(@this));

        // 创建一个Des对象
        using var des = DES.Create();
        var pair = des.GetKeyIv(key, iv);

        // 创建一个解密器来执行流转换
        var decryptor = des.CreateDecryptor(pair.key, pair.iv);

        // 创建用于解密的流
        using var msDecrypt = new MemoryStream(@this.ToArray());
        using var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);

        // 从解密流中读取解密后的字节并将其放入字节数组中
        using var originalStream = new MemoryStream();
        var buffer = new byte[256];
        int readBytes;
        while ((readBytes = csDecrypt.Read(buffer, 0, buffer.Length)) > 0)
        {
            originalStream.Write(buffer, 0, readBytes);
        }

        originalStream.Flush();
        return originalStream.ToArray();
    }

    /// <summary>
    /// 使用GZip算法压缩字节数组
    /// </summary>
    /// <param name="this">要压缩的字节数组</param>
    /// <returns>压缩后的字节数组</returns>
    public static byte[] GZipCompress(this ReadOnlySpan<byte> @this)
    {
        using var outStream = new MemoryStream();
        using var zipStream = new GZipStream(outStream, CompressionMode.Compress, true);
        zipStream.Write(@this);
        zipStream.Close(); //很重要，必须关闭，否则无法正确解压

        outStream.Position = 0;
        return outStream.ToArray();
    }

    /// <summary>
    /// 使用GZip算法解压缩字节数组
    /// </summary>
    /// <param name="this">要解压缩的字节数组</param>
    /// <returns>解压缩后的字节数组</returns>
    public static byte[] GZipDecompress(this ReadOnlySpan<byte> @this)
    {
        using var inputStream = new MemoryStream();
        inputStream.Write(@this);
        inputStream.Position = 0;
        using var outStream = new MemoryStream();
        using var zipStream = new GZipStream(inputStream, CompressionMode.Decompress);
        zipStream.CopyTo(outStream);
        zipStream.Close();

        outStream.Position = 0;
        return outStream.ToArray();
    }

    /// <summary>
    /// 使用Deflate算法压缩字节数组
    /// </summary>
    /// <param name="this">要压缩的字节数组</param>
    /// <returns>压缩后的字节数组</returns>
    public static byte[] ZipCompress(ReadOnlySpan<byte> @this)
    {
        using var outStream = new MemoryStream();
        using var zipStream = new DeflateStream(outStream, CompressionMode.Compress, true);
        zipStream.Write(@this);
        zipStream.Close(); //很重要，必须关闭，否则无法正确解压

        outStream.Position = 0;
        return outStream.ToArray();
    }

    /// <summary>
    /// 使用Deflate算法解压缩字节数组
    /// </summary>
    /// <param name="this">要解压缩的字节数组</param>
    /// <returns>解压缩后的字节数组</returns>
    public static byte[] ZipDecompress(ReadOnlySpan<byte> @this)
    {
        using var inputStream = new MemoryStream();
        inputStream.Write(@this);
        inputStream.Position = 0;
        using var outStream = new MemoryStream();
        using var zipStream = new DeflateStream(inputStream, CompressionMode.Decompress);
        zipStream.CopyTo(outStream);
        zipStream.Close();

        outStream.Position = 0;
        return outStream.ToArray();
    }
}
